package com.apiauto.test_case;

import java.io.File;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;

import com.alibaba.fastjson.JSONObject;
import com.apiauto.data_util.CaseUtil;
import com.apiauto.data_util.DBCheckUtil;
import com.apiauto.data_util.ExcelUtil;
import com.apiauto.data_util.RestUtil;
import com.apiauto.http_util.Request;
import com.apiauto.http_util.Response;
import com.apiauto.log.Log;
import com.apiauto.pojo.Rest;
import com.apiauto.pojo.WriteData;
import com.apiauto.util.Constant;
import com.apiauto.util.MockUtil;
import com.google.common.base.Strings;
import com.apiauto.util.TestNGRetry;

/**
 * PS:用例基类
 * 
 * @author lkk
 *
 */
public class BaseCase {
	// 创建可缓存线程池对象
	public static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

	/**
	 * PS:公有测试方法
	 * 
	 * @param apiNum               接口编号
	 * @param caseNum              用例编号
	 * @param requestHeaders       请求头
	 * @param requestDataType      数据类型
	 * @param requestData          请求数据
	 * @param checkPoint           检查点
	 * @param expectResponseResult 预期响应结果
	 * @param correlation          提取参数公式
	 * @param checkExcuteSql       检查脚本
	 */
	// retryAnalyzer= TestNGRetry.class指定用例重跑机制类
	@Test(dataProvider = "datas", testName = "公用方法", retryAnalyzer = TestNGRetry.class)
	public void baseTest(String apiNum, String caseNum, String requestHeaders, String requestDataType,
			String requestData, String checkPoint, String expectResponseResult, String correlation,
			String checkExcuteSql) {
		// 发送请求以及脚本校验,获取响应对象
		CloseableHttpResponse response = sendRequest(apiNum, caseNum, requestHeaders, requestDataType, requestData,
				checkExcuteSql);
		// 获取响应头
		String headers = JSONObject.toJSONString(Response.getResponseHeaders(response));
		// 获取响应内容
		String responseBody = Response.getResponseEntity(response);
		// 判断是否存在提取变量公式,若存在将变量存入集合
		if (!Strings.isNullOrEmpty(correlation)) {
			// 提取依赖参数存入集合
			CaseUtil.extractVariable(responseBody, correlation);
		}
		assertAndWrite(caseNum, responseBody, checkPoint);
	}

	/**
	 * PS:启动mock服务
	 */
	@BeforeSuite
	public void startOperation() {
		cachedThreadPool.execute(new Runnable() {
			@Override
			public void run() {
				MockUtil.startMock();
			}
		});
	}

	/**
	 * PS:执行批量写入
	 */
	@AfterSuite
	public void lastOperation() {
		// 写入数据
		ExcelUtil.batchWriteData(Constant.EXCEL_PATH);
		Log.info("-------写入数据成功--------------");
		// 终止线程mock服务
		cachedThreadPool.shutdown();
		Log.info("-------MOCK服务已关闭--------------");
	}

	/**
	 * PS:发送请求,获取响应对象(有sql脚本则进行校验)
	 * 
	 * @param apiNum          接口编号
	 * @param caseNum         用例编号
	 * @param requestHeaders  请求头
	 * @param requestDataType 请求类型
	 * @param requestData     请求数据
	 * @param checkExcuteSql  验证脚本
	 * @return 响应对象
	 */
	public CloseableHttpResponse sendRequest(String apiNum, String caseNum, String requestHeaders,
			String requestDataType, String requestData, String checkExcuteSql) {
		// 获取请求方式
		String method = RestUtil.getResultByfield("apiNum", apiNum, "method", Rest.class, ExcelUtil.restList);
		// 获取协议版本
		String protocol = RestUtil.getResultByfield("apiNum", apiNum, "protocol", Rest.class, ExcelUtil.restList);
		// 获取主机地址
		String host = RestUtil.getResultByfield("apiNum", apiNum, "host", Rest.class, ExcelUtil.restList);
		// 获取访问路径
		String path = RestUtil.getResultByfield("apiNum", apiNum, "path", Rest.class, ExcelUtil.restList);
		// 获取URL
		String url = host + path;
		// sql验证脚本不为空时执行前验证
		if (!Strings.isNullOrEmpty(checkExcuteSql)) {
			String pre_ExecutionResultsResult = DBCheckUtil.doQuery(checkExcuteSql);
			ExcelUtil.writeDataList
					.add(new WriteData("用例", caseNum, "Pre_ExecutionResults", pre_ExecutionResultsResult));
		}
		// 替换变量为值
		requestHeaders = CaseUtil.replaceVariable(requestHeaders);
		requestData = CaseUtil.replaceVariable(requestData);
		Log.info("请求头部:" + requestHeaders);
		Log.info("请求数据:" + requestData);
		// 发送请求获取响应对象
		CloseableHttpResponse response = Request.doServer(method, requestHeaders, protocol, url, requestDataType,
				requestData);
		// sql验证脚本不为空时执行后验证
		if (!Strings.isNullOrEmpty(checkExcuteSql)) {
			String post_ExecutionResults = DBCheckUtil.doQuery(checkExcuteSql);
			ExcelUtil.writeDataList.add(new WriteData("用例", caseNum, "Post_ExecutionResults", post_ExecutionResults));
		}
		return response;
	}
	/**
	 * PS:断言及写入工作(公用方法)
	 * @param caseNum 写入的用例编号
	 * @param responseBody 断言主体
	 * @param checkPoint 检查点
	 */
	public void assertAndWrite(String caseNum, String responseBody, String checkPoint) {
		// 断言结果
		boolean flag = false;
		try {
			// 获取断言结果
			flag = CaseUtil.verifycheckPoint(responseBody, checkPoint);
		} catch (AssertionError e) {
			throw new AssertionError("-----用例" + caseNum + "执行未通过," + e.getMessage() + "-----");
		} finally {
			// 新建需要写入的结果对象
			WriteData actualResponseResult = null;
			// 判断是否通过,决定写入excel内容
			if (flag == true) {
				actualResponseResult = new WriteData("用例", caseNum, "ActualResponseResult", "pass");
			} else {
				actualResponseResult = new WriteData("用例", caseNum, "ActualResponseResult", responseBody);
			}
			// 将该对象加入最终写入集合中
			ExcelUtil.writeDataList.add(actualResponseResult);
		}

	}
}
